#include <MeggyJrSimple.h>

/*
 PLEASE NOTE: the program will require the SDL libraries amongst other things
 so refer to http://flippinbits.com/twiki/meggy for more information.
 */

/*
  MeggyJrSimple.h - Meggy Jr RGB Simulator library for Arduino
   The Meggy Jr Simplified Library Simulator (MJSL)
   
  Version 1.2 - 10/1/2009
  Copyright (c) 2009 Ken A. Corey. All right reserved.
  http://flippinbits.com/twiki/meggy
  Based on code from:
  Copyright (c) 2008 Windell H. Oskay.  All right reserved.
  http://www.evilmadscientist.com/

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
	  
    A rather stupid simulator of the Meggy library, so that I could
    debug my programs on a full-powered computer, and only *then* put
    them onto my meggy.

    If you *must* have a tight loop, at least have the decency to allow SDL to
    track key state like this:

    while (looping) {
#ifdef MeggySimulator
    CheckSDLEvents();
    CheckButtonsDown();
#endif
      doingSomething();
    }

*/

#define XSCREENOFFSET 150
#define YSCREENOFFSET 150
#define LEDSPACE 25
#define LEDSIZE 20
#define AUXLEDSIZE 6
#define AUXLEDXOFFSET 3
#define AUXLEDYOFFSET 20
#define BORDERSIZE 2

byte GameSlate[8][8];       
byte lastButtonState;

byte Button_A;		 
byte Button_B;
byte Button_Up;
byte Button_Down;
byte Button_Left;
byte Button_Right;

unsigned long SoundStopTime;
byte  ToneInProgress;
 
//Color lookup Table
byte ColorTable[26][3] = 
{
  { MeggyDark  },  
  { MeggyRed  }  ,
  { MeggyOrange  },
  { MeggyYellow  },
  { MeggyGreen  },
  { MeggyBlue  } ,
  { MeggyViolet  },
  { MeggyWhite  },  
  { MeggyDimRed  },  
  { MeggyDimOrange  },  
  { MeggydimYellow  },  
  { MeggyDimGreen  },  
  { MeggydimAqua  },  
  { MeggyDimBlue  },  
  { MeggydimViolet  },   
  { MeggyCursorColor},      //Extra bright cursor position color (not white).
  {0,0,0},					//CustomColor0 (dark, by default)
  {0,0,0},			        //CustomColor1 (dark, by default)
  {0,0,0},					//CustomColor2 (dark, by default)
  {0,0,0},					//CustomColor3 (dark, by default)	
  {0,0,0},					//CustomColor4 (dark, by default)
  {0,0,0},					//CustomColor5 (dark, by default)
  {0,0,0},					//CustomColor6 (dark, by default)
  {0,0,0},					//CustomColor7 (dark, by default)
  {0,0,0},					//CustomColor8 (dark, by default)
  {0,0,0}					//CustomColor9 (dark, by default)
}; 


  // Stop the note if its completion time has come.
void Tone_Update(void)
{
}

void HandleSDLKeyDown(SDL_KeyboardEvent key)
{
  int a=0,b=0;
  if (key.type == SDL_KEYDOWN) {
      switch(key.keysym.sym){
        case SDLK_LEFT:
         Meg.mykeystate |= 16;
          break;
        case SDLK_RIGHT:
         Meg.mykeystate |=32;
          break;
        case SDLK_UP:
         Meg.mykeystate |= 4;
          break;
        case SDLK_DOWN:
         Meg.mykeystate |= 8;
          break;
        case SDLK_1:
         Meg.mykeystate |= 1;
          break;
        case SDLK_LSHIFT:  // These are here so that if you want to break without modifying the
        case SDLK_RSHIFT:  // keystate you can.  Simply set a breakpoint here:
          a=b;
          break;
        case SDLK_2:
         Meg.mykeystate |= 2;
          break;
        default:
          break;
      }
  } else if (key.type == SDL_KEYUP) {
      switch(key.keysym.sym){
        case SDLK_LEFT:
         Meg.mykeystate ^= 16;
          break;
        case SDLK_RIGHT:
         Meg.mykeystate ^=32;
          break;
        case SDLK_UP:
         Meg.mykeystate ^= 4;
          break;
        case SDLK_DOWN:
         Meg.mykeystate ^= 8;
          break;
        case SDLK_1:
         Meg.mykeystate ^= 1;
          break;
        case SDLK_2:
         Meg.mykeystate ^= 2;
          break;
        default:
          break;
      }
  }
  // If you need to see it as it happens, uncomment these.
  //SetAuxLEDs((unsigned char)Meg.mykeystate);
  //SDL_Flip (Meg.Meg.screen);     
}

void CheckButtonsDown()
	{ 
	 byte i = (byte)Meg.mykeystate; 
	 
 	 Button_B  = (i & 1);      
     Button_A = (i & 2);     
     Button_Up = (i & 4);
     Button_Down = (i & 8);
     Button_Left = (i & 16);
     Button_Right = (i & 32);
	 
	 lastButtonState = i; 
	}
	 
void CheckButtonsPress()
	{
	 byte j;
	 byte i = (byte)Meg.mykeystate;
	 j = i & ~(lastButtonState);  // What's changed?
	 
 	 Button_B  = (j & 1);      
     Button_A = (j & 2);     
     Button_Up = (j & 4);
     Button_Down = (j & 8);
     Button_Left = (j & 16);
     Button_Right = (j & 32);
	 
	 lastButtonState = i;
	}
 
 
// Write a byte to the Auxiliary LED set at the top of the LED matrix display.  
void SetAuxLEDs(byte InputLEDs)
	{
//		Meg.AuxLEDs = InputLEDs;
    byte i;
    unsigned long colourlit, colourdark, colourborder;
    byte bit;
    SDL_Rect dstrect;

    colourlit = SDL_MapRGB(Meg.screen->format, 0xff, 0xff, 0x00);
    colourdark = SDL_MapRGB(Meg.screen->format, 0, 0, 0);
    colourborder = SDL_MapRGB(Meg.screen->format, 0xff, 0xff, 0xff);

    bit=1;
    for(i=0;i<8;i++) {
      dstrect.x = XSCREENOFFSET + AUXLEDXOFFSET + i*LEDSPACE;
      dstrect.y = YSCREENOFFSET - AUXLEDYOFFSET;
      dstrect.w = AUXLEDSIZE;
      dstrect.h = AUXLEDSIZE;
      if (bit & InputLEDs) {
  			SDL_FillRect(Meg.screen, &dstrect,colourlit);
      } else {
			  SDL_FillRect(Meg.screen, &dstrect,colourborder);
			  dstrect.x++; dstrect.y++;
			  dstrect.w -= BORDERSIZE; dstrect.h -= BORDERSIZE;
			  SDL_FillRect(Meg.screen, &dstrect,colourdark);
      }
      bit<<=1;
    }
    Meg.LEDstate = InputLEDs;
	}

 

// Write a byte to the Auxiliary LED set at the top of the LED matrix display.  
// This version reverses bit order, so you can call it with an explicit binary number
void SetAuxLEDsBinary(byte n)
{
  SetAuxLEDs(n);
}

 


// Simple function to color in a pixel at position (x,y,color):
void DrawPx(byte xin, byte yin, byte color)
{
		GameSlate[xin][yin] = color;
}



// function to read color of pixel at position (x,y):
byte ReadPx(byte xin, byte yin)
{   
	return	GameSlate[xin][yin];
}

void MeggySimFillSlate(byte colour)
{
  byte i;
  byte j;
  i = 0;
  while (i < 8) { 
    j = 0;
    while ( j < 8)
    {
      GameSlate[i][j] = colour;
      j++;
    }
    i++;
  }
}

//Empty the Game Slate:
void ClearSlate(void)
{
  MeggySimFillSlate(0);
}

void MeggySimSetPxClr(byte x, byte y, byte rgb[3])
{
  SDL_Rect dstrect;
  unsigned long colour;

  dstrect.x=XSCREENOFFSET+x*LEDSPACE;
  dstrect.y=YSCREENOFFSET+(7-y)*LEDSPACE;
  dstrect.w=LEDSIZE;
  dstrect.h=LEDSIZE;

/*
colour = SDL_MapRGB(Meg.screen->format, 
          255,
          255,
          255
          );
  SDL_FillRect(Meg.screen, &dstrect,colour);
  SDL_Flip (Meg.screen);     
*/
  colour = SDL_MapRGB(Meg.screen->format, 
          rgb[0]<<4,
          rgb[1]<<4,
          rgb[2]<<4
          );
  SDL_FillRect(Meg.screen, &dstrect,colour);
}

// DisplaySlate() :: Write the Game Slate to the Display Memory it.
// This function looks up each color number (name) stored in the Game Slate,
// retreives its R,G,B components from the color table, and writes them to the
// R,G,B parts of the display memory.

void DisplaySlate (void) {	     
     byte i,j;
     SDL_Rect dstrect;
     unsigned long colour;
     byte c,d;
     
     dstrect.x=0;
     dstrect.y=0;
     for(i=0;i<8;i++) {
       for (j=0;j<8;j++) {
		     d=0;
         dstrect.x=XSCREENOFFSET+i*LEDSPACE;
         dstrect.y=YSCREENOFFSET+j*LEDSPACE;
		     dstrect.w=LEDSIZE;
		     dstrect.h=LEDSIZE;
         c = GameSlate[i][7-j];
		     if (c) {
			      colour = SDL_MapRGB(Meg.screen->format, 
                                ColorTable[c][0],
                                ColorTable[c][1],
                                ColorTable[c][2]
                                );
			      SDL_FillRect(Meg.screen, &dstrect,colour);
		     } else {
			      colour = SDL_MapRGB(Meg.screen->format,255,255,255); 
			      SDL_FillRect(Meg.screen, &dstrect,colour);
			      dstrect.x++; dstrect.y++;
			      dstrect.w -= BORDERSIZE; dstrect.h -= BORDERSIZE;
			      SDL_FillRect(Meg.screen, &dstrect,0);
		     }
       }
     }
     SetAuxLEDs(Meg.LEDstate);
     SDL_Flip (Meg.screen);     
}  
  
Uint32 SDL_GetPixel(SDL_Surface *surf, int x, int y)
{
	//This function returns pixels color
	int bpp = surf->format->BytesPerPixel;
	Uint8 *p = (Uint8 *)surf->pixels + y * surf->pitch + x * bpp;
	
	switch (bpp)
	{
		case 1:
			return *p;

		case 2:
			return *(Uint16 *)p;

		case 3:
			if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
				return p[0] << 16 | p[1] << 8 | p[2];
			else
				return p[0] | p[1] << 8 | p[2] << 16;
		
		case 4:
			return *(Uint32 *)p;
		
		default:
			return 0;
	}
}

byte MeggySimGetPixelR(byte x, byte y)
{
  unsigned long pixel = SDL_GetPixel(Meg.screen, XSCREENOFFSET + x*LEDSPACE + (LEDSIZE>>1), YSCREENOFFSET + y*LEDSPACE + (LEDSIZE>>1));
  byte r,g,b;

  SDL_GetRGB(pixel,Meg.screen->format,&r, &g, &b);

  return r;
}

byte MeggySimGetPixelG(byte x, byte y)
{
  unsigned long pixel = SDL_GetPixel(Meg.screen, XSCREENOFFSET + x*LEDSPACE + (LEDSIZE>>1), YSCREENOFFSET + y*LEDSPACE + (LEDSIZE>>1));
  byte r,g,b;

  SDL_GetRGB(pixel,Meg.screen->format,&r, &g, &b);

  return g;
}

byte MeggySimGetPixelB(byte x, byte y)
{
  unsigned long pixel = SDL_GetPixel(Meg.screen, XSCREENOFFSET + x*LEDSPACE + (LEDSIZE>>1), YSCREENOFFSET + y*LEDSPACE + (LEDSIZE>>1));
  byte r,g,b;

  SDL_GetRGB(pixel,Meg.screen->format,&r, &g, &b);

  return b;
}

 
  /*
 Sound output:
 
 Frequency is given by the following formula:
 
 f= 8 MHz/divisor, so divisor = 8 MHz/f.   (Round to nearest.)
 Maximum divisor: 65535, so min. frequency is 122 Hz
 
 Example: for 440 Hz, divisor = 18182
  
 */
  
   // "Cheater" functions
 #define SoundOn()    Meg.SoundState(1)
 #define SoundOff()   Meg.SoundState(0)
 #define MakingSound  Meg.ToneInProgress
 
 // other sound functions:
void Tone_Start(unsigned int divisor, unsigned int duration_ms)
{
//  Meg.StartTone(divisor, duration_ms);
}


unsigned long millis()
{
       return SDL_GetTicks();
}

void delay(unsigned long delaymilis)
{
     SDL_Delay(delaymilis);
}
 

void EditColor(byte WhichColor, byte RedComponent, byte GreenComponent, byte BlueComponent)
{
   ColorTable[WhichColor][0] = RedComponent<<4;
   ColorTable[WhichColor][1] = GreenComponent<<4;
   ColorTable[WhichColor][2] = BlueComponent<<4;
 } 




void MeggyJrSimpleSetup(void) 
  {
  
    Meg.LEDstate=0;
    Meg.mykeystate=0;
    ClearSlate();
/*
		Meg = MeggyJr();  
		
	    lastButtonState = Meg.GetButtons();
		
		 Meg.StartTone(0, 0);
		 Tone_Update();
		 SoundOff();
*/		 
  }
  
void CheckSDLEvents()
{
  SDL_Event event;

  /* Check for events */
  while (SDL_PollEvent (&event))
  {
      switch (event.type)
      {
      case SDL_KEYDOWN:
      case SDL_KEYUP:
        HandleSDLKeyDown(event.key);
          break;
      case SDL_QUIT:
          Meg.eventdone = 1;
          break;
      default:
          break;
      }
  }
}

void 
randomSeed(unsigned int seed)
{
  srand(seed);
}

unsigned long
random(unsigned int min, unsigned int max)
{
  unsigned long diff = max-min;
  diff = (rand()%diff)+min;
  return diff;
}

byte MockGetButtons(void) 
{
  return (byte)Meg.mykeystate;
}


void
MeggySimSetup()
{
  Meg.mykeystate=0;
  Meg.LEDstate = 0;
  Meg.eventdone=0;
  Meg.GetButtons = &MockGetButtons;
  Meg.GetPixelR = &MeggySimGetPixelR;
  Meg.GetPixelG = &MeggySimGetPixelG;
  Meg.GetPixelB = &MeggySimGetPixelB;
  Meg.SetPxClr = &MeggySimSetPxClr;
}

int
main (int argc, char *argv[])
{
    char *msg="no message";

    /* Initialize SDL */
    if (SDL_Init (SDL_INIT_VIDEO) < 0)
    {
        sprintf (msg, "Couldn't initialize SDL: %s\n", SDL_GetError ());
        MessageBox (0, (LPCWSTR)msg, (LPCWSTR)"Error", MB_ICONHAND); 
        free (msg);
        exit (1);
    }
    atexit (SDL_Quit);

    /* Set 640x480 16-bits video mode */
    Meg.screen = SDL_SetVideoMode (640, 480, 16, SDL_SWSURFACE | SDL_DOUBLEBUF);
    if (Meg.screen == NULL)
    {
        sprintf (msg, "Couldn't set 640x480x16 video mode: %s\n",
          SDL_GetError ());
        MessageBox (0, (LPCWSTR)msg, (LPCWSTR)"Error", MB_ICONHAND); 
        free (msg);
        exit (2);
    }
    SDL_WM_SetCaption ("MeggyJr RGB Simulator", NULL);

    MeggySimSetup();

    // Call the setup for the meggy program
    setup();
    Meg.eventdone = 0;
    while (!Meg.eventdone)
    {
        CheckSDLEvents();

        // call the loop in the meggy program
        loop();
        SDL_Flip (Meg.screen);     
    }

    return 0;
}
